package analyzer

import (
    "plugin"
    "path/filepath"
    "github.com/astaxie/beego/logs"
    "fmt"
    "time"
    "cloud_worm/util/umodels/mysqldb"
    "cloud_worm/util/umodels/mysqldb/dbdata"
    "cloud_worm/wormweb/controllers/ctrutil"
)

/**
 @Description：weapon log分析的关键要素和func
 */
type ComputeItem struct {
    Name        string

    WormId       string
    Ip          string
    WeaponId    int64
    TaskId      string
}

/**
 @Description：weapon log的分析
 @Param:
 @Return：
 */
func (c *ComputeItem) weaponLogDeal() (string, error) {
    soPath := filepath.Join(ctrutil.UploadSoDir, c.Name + ".so")
    p, err := plugin.Open(soPath)
    if err != nil {
        return "", fmt.Errorf("open lib`%s` so error: %s", c.Name, err.Error())
    }

    // get function
    f, err := p.Lookup(ctrutil.AnalyzerFunc)
    if err != nil {
        return "", fmt.Errorf("lookup analyzer function error: %s", err.Error())
    }

    // 得到源数据
    wpLog := &dbdata.WeaponLog{
        WormId: c.WormId,
        WeaponId: c.WeaponId,
        TaskId: c.TaskId,
    }
    err = wpLog.Read("worm_id", "weapon_id", "task_id")
    if err != nil {
        return "", fmt.Errorf("the weapon log is no exist")
    }

    // 数据分析
    count, detail, nickname, err := f.(func(string) (int, string, string, error))(wpLog.Output)
    if err != nil {
        return "", fmt.Errorf("do lib Analyzer error: %s", err.Error())
    }

    // 数据写入表中
    machineInfo := &dbdata.MachineInfo{
        Ip: c.Ip,
        Name: c.Name,
    }
    err = machineInfo.Read("ip", "name")
    if err != nil {
        // 不存在，需要执行insert操作
        machineInfo.Count = count
        machineInfo.Detail = detail
        machineInfo.Nickname = nickname
        machineInfo.Created = time.Now().Unix()
        machineInfo.Updated = machineInfo.Created
        err = machineInfo.Insert()
        if err != nil {
            logs.Error(fmt.Sprintf("insert '%s' '%s' info failed: %s", c.Ip, c.Name, err.Error()))
        }
    } else {
        // 存在执行更新操作
        machineInfo.Count = count
        machineInfo.Detail = detail
        machineInfo.Nickname = nickname
        machineInfo.Updated = time.Now().Unix()
        err = machineInfo.Update("count", "detail", "updated", "nickname")
        if err != nil {
            logs.Error(fmt.Sprintf("update '%s' '%s' info failed: %s", c.Ip, c.Name, err.Error()))
        }
    }

    return nickname, nil
}

/**
 @Description：将之前的alert设置为无效
 @Param:
 @Return：
 */
func (c *ComputeItem) setHistoryAlertInvalid() error {
    ma := &dbdata.WeaponAlertInfo{}
    return ma.SetInvalidByIPAndName(c.Ip, c.Name)
}

/**
 @Description：weapon alert的分析
 @Param:
 @Return：
 */
func (c *ComputeItem) weaponAlertDeal(nickname string) error {
    // 基于task相关信息，得到alert logs
    alertLog := &dbdata.WeaponAlertLog{}
    alertLogs, err := alertLog.Select(mysqldb.SelectOffsetZero, mysqldb.SelectLimitAll, c.WormId, c.WeaponId, c.TaskId)
    if err != nil {
        return fmt.Errorf("alert log select error: %s", err.Error())
    }

    // 处理新的alert之前，需要将历史的设置为invalid
    err = c.setHistoryAlertInvalid()
    if err != nil {
        return fmt.Errorf("set history alert invalid: %s", err.Error())
    }

    var optionCount int
    var optionFailed int
    var optionOk int
    for _, item := range alertLogs {
        machineAlert := &dbdata.WeaponAlertInfo{
            Ip: c.Ip,
            Name: c.Name,
            OptionId: item.OptionId,
        }
        err = machineAlert.Read("ip", "name", "option_id")
        if err != nil {
            // 不存在，执行insert插入
            machineAlert.OptionName = item.OptionName
            machineAlert.Result = item.Result
            machineAlert.ExtraMsg = item.AlertMsg
            machineAlert.Created = time.Now().Unix()
            machineAlert.Updated = machineAlert.Created
            err = machineAlert.Insert()
            if err != nil {
                logs.Error(fmt.Sprintf("machine alert info insert error: %s", err.Error()))
            }
        } else {
            // 存在，执行update操作
            machineAlert.OptionName = item.OptionName
            machineAlert.Result = item.Result
            machineAlert.ExtraMsg = item.AlertMsg
            machineAlert.Updated = time.Now().Unix()
            machineAlert.Status = mysqldb.StatusValid
            err = machineAlert.Update("option_name", "result", "extra_msg", "updated", "status")
            if err != nil {
                logs.Error(fmt.Sprintf("machine alert info alert error: %s", err.Error()))
            }
        }

        // 准备统计数据
        optionCount = optionCount + 1
        if item.Result == 0 {
            optionFailed = optionFailed + 1
        } else {
            optionOk = optionOk + 1
        }
    }

    // 更新统计数据
    alertCount := &dbdata.WeaponAlertCount{
        Ip: c.Ip,
        Name: c.Name,
    }
    err = alertCount.Read("ip", "name")
    if err != nil {
        // 不存在，执行insert插入
        alertCount.Nickname = nickname
        alertCount.OptionCount = optionCount
        alertCount.OptionFailed = optionFailed
        alertCount.Created = time.Now().Unix()
        alertCount.Updated = alertCount.Created
        err = alertCount.Insert()
        if err != nil {
            logs.Error(fmt.Sprintf("machine alert count insert error: %s", err.Error()))
        }
    } else {
        // 存在，执行update操作
        alertCount.Nickname = nickname
        alertCount.OptionCount = optionCount
        alertCount.OptionFailed = optionFailed
        alertCount.Updated = time.Now().Unix()
        err = alertCount.Update("nickname", "option_count", "option_failed", "option_ok", "updated")
        if err != nil {
            logs.Error(fmt.Sprintf("machine alert count update error: %s", err.Error()))
        }
    }

    return nil
}

/**
 @Description：调用分析，将数据写入到db中
 @Param:
 @Return：
 */
func (c *ComputeItem) Deal()  {

    nickname, err := c.weaponLogDeal()
    if err != nil {
        logs.Error(fmt.Sprintf("weapon log deal for '%s': %s", c.Ip, err.Error()))
        return
    }
    err = c.weaponAlertDeal(nickname)
    if err != nil {
        logs.Error(fmt.Sprintf("weapon alert deal for '%s': %s", c.Ip, err.Error()))
        return
    }

    return
}